{
    // 块级作用域
    let a = 1;
    // 全局作用域
    var b = 2;
    console.log(a); // 1
}

// console.log(a); // 报错
console.log(b); // 2

// **************************************************************************

// 两个 i 的作用域不一样，括号的 i 在父级作用域。
for (let i = 0; i < 3; i++) {
    // 内部的 i 属于单独的子作用域。
    let i = 'abc'
    console.log(i); // abc abc abc
}

// **************************************************************************

// 【暂时性死区】

// 例子1：
var tmp = 1;

if (true) {
    tmp = 'abc' // 报错（当前块中已存在相同变量，所以不会使用外部var声明的变量，但是由于声明还未成功，所以赋值报错，let const 都是如此）
    let tmp;
}

// 例子2：代码报错时也不会继续往下执行，以下只是表示一个声明和赋值的具体流程。
if (true) {
    tmp = 'abc' // 赋值报错（变量未声明）
    console.log(tmp); // 报错（不存在的变量）

    let tmp;
    console.log(tmp); // undefined（已声明未赋值）

    tmp = 123;
    console.log(tmp); // 123
}

// **************************************************************************

// 【不允许重复声明】

function func(x) {
    // 这里的形参 x 想当于是，在函数内部这里，声明了一个变量 x。
    // let x; 如果再次显示声明，调用函数时则报错。
}

// **************************************************************************

// 【ES6 的块级作用域 】

// 例子1：
// 不会报错，因为在不同的块级作用域中，变量 x 互不干扰。
{
    {
        {
            let x = 1
            {
                let x = 2
            }
        }
    }
}

// 例子2：
// 在浏览器的 ES6 环境，块级作用域的 函数声明 执行过程（JS 引擎扫描代码时）。
function f() { console.log('I am outside!'); }

(function () {
  var f = undefined; // 隐式创建一个 f 的变量，并且赋值为 undefined。
  if (false) {
    // 这里的 函数声明 在 if 块不会执行（因为 if 为 false），所以外部的 f 变量还是 undefined；if 为 true 则外部 f 变量为当前 f 函数。
    function f() { console.log('I am inside!'); }
  }

  f(); // 报错（如果 if 为 true，则输出 'I am inside!'）
}());